/*
File:		Game.cpp
Course:		SGD 1303
Author:		dmonroe
Purpose:	CGame class controls the entire game
& manages the SGD Wrappers
*/


#include "Game.h"


//**************************************************
// SGD Wrappers
#include "../SGD Wrappers/CSGD_Direct3D.h"
#include "../SGD Wrappers/CSGD_DirectInput.h"
#include "../SGD Wrappers/CSGD_TextureManager.h"
#include "../SGD Wrappers/CSGD_XAudio2.h"
#include "../SGD Wrappers/CSGD_MessageSystem.h"
#include "../SGD Wrappers/CSGD_EventSystem.h"
#include "../SGD Wrappers/SGD_String.h"
#include "../SGD Wrappers/SGD_Math.h"

#include "EntityManager.h"
#include "Entity.h"
#include "Message.h"

#include <cassert>



//**************************************************
// SINGLETON
//	- instantiate the singleton member
/*static*/ CGame*	CGame::s_pInstance	= nullptr;


// GetInstance
//	- allocate the CGame object when needed
/*static*/ CGame*	CGame::GetInstance		( void )
{
	if( s_pInstance == nullptr )
		s_pInstance = new CGame;


	return s_pInstance;
}

// DeleteInstance
//	- deallocate the CGame object
/*static*/ void		CGame::DeleteInstance	( void )
{
	delete s_pInstance;
	s_pInstance = nullptr;
}



//**************************************************
// CONSTRUCTOR
CGame::CGame(void)
{
	// Set ALL the data members to 'safe' values

	m_hWnd			= nullptr;
	m_hInstance		= nullptr;
	m_nWidth		= 1;
	m_nHeight		= 1;
	m_bWindowed		= true;

	m_pD3D			= nullptr;
	m_pDI 			= nullptr;
	m_pTM 			= nullptr;
	m_pXA 			= nullptr;
	m_pMS			= nullptr;
	m_pES			= nullptr;
	m_pEM			= nullptr;
	

	// -1 == INVALID_ASSET

	m_fFireTimer	= 0.0f;

	m_dwCurrTime	= 0;

	m_unFPS			= 0;
	m_unFrames		= 0;
	m_fFPSTimer		= 0;


	// Seed random number generator only once
	srand( (unsigned int)GetTickCount() );
}


//**************************************************
// DESTRUCTOR
CGame::~CGame(void)
{
}




//**************************************************
// Initialize
//	- initialize the wrappers & game entities
void CGame::Initialize( HWND hWnd, HINSTANCE hInstance,
	int nWindowWidth, int nWindowHeight,
	bool bWindowed )
{
	// Store the parameters
	m_hWnd			= hWnd;
	m_hInstance		= hInstance;
	m_nWidth		= nWindowWidth;
	m_nHeight		= nWindowHeight;
	m_bWindowed		= bWindowed;



	// Store the SGD Wrapper singletons
	m_pD3D			= CSGD_Direct3D::GetInstance();
	m_pDI 			= CSGD_DirectInput::GetInstance();
	m_pTM 			= CSGD_TextureManager::GetInstance();
	m_pXA 			= CSGD_XAudio2::GetInstance();
	m_pMS			= CSGD_MessageSystem::GetInstance();
	m_pES			= CSGD_EventSystem::GetInstance();
	m_pEM			= CEntityManager::GetInstance();

	// Initialize the SGD Wrappers
	m_pD3D->Initialize( m_hWnd, m_nWidth, m_nHeight, m_bWindowed, false );
	m_pDI->Initialize( m_hWnd, m_hInstance, DI_KEYBOARD /*| DI_MOUSE | DI_JOYSTICKS*/ );
	m_pTM->Initialize( m_pD3D->GetDirect3DDevice(), m_pD3D->GetSprite() );
	m_pXA->Initialize();

	// Initialize the Message System with the callback function
	m_pMS->InitMessageSystem( &MessageProc );



	// Load the resources RELATIVE TO THE PROJECT!!!


	m_fFireTimer	= 0.0f;

	m_dwCurrTime	= GetTickCount();

	m_unFPS			= 60;
	m_unFrames		= 0;
	m_fFPSTimer		= 0;
}


//**************************************************
// Main
//	- run one frame of the game
bool CGame::Main( void )
{
	if( Input() == false )
		return false;


	Update();
	Render();
	return true;
}


//**************************************************
// Terminate
//	- clean up the game
void CGame::Terminate( void )
{
	// Shutdown the EventSystem
	if( m_pES != nullptr )
	{
		m_pES->ShutdownEventSystem();
		m_pES = nullptr;
	}

	// Shutdown the Message System
	if( m_pMS != nullptr )
	{
		m_pMS->ShutdownMessageSystem();
		m_pMS = nullptr;
	}

	// Shutdown the Entity Manager
	if( m_pEM != nullptr )
	{
		m_pEM->RemoveAll();
		CEntityManager::DeleteInstance();
		m_pEM = nullptr;
	}




	// Unload resources


	// -1 == INVALID_ASSET


	// Terminate the SGD Wrappers
	if( m_pXA != nullptr )
		m_pXA->Terminate();

	if( m_pTM != nullptr )
		m_pTM->Terminate();

	if( m_pDI != nullptr )
		m_pDI->Terminate();

	if( m_pD3D != nullptr )
		m_pD3D->Terminate();


	// Reset data members to 'safe' values
	m_hWnd			= nullptr;
	m_hInstance		= nullptr;
	m_nWidth		= 1;
	m_nHeight		= 1;
	m_bWindowed		= true;

	m_pD3D			= nullptr;
	m_pDI 			= nullptr;
	m_pTM 			= nullptr;
	m_pXA 			= nullptr;
}


//**************************************************
// Input
//	- handle user input
bool CGame::Input( void )
{
	// Read the DirectInput devices
	m_pDI->ReadDevices();


	// Exit the game when the user presses Escape
	if( m_pDI->KeyPressed( DIK_ESCAPE ) == true )
		return false;


	// Keep playing
	return true;
}


//**************************************************
// Update
//	- update game entities
void CGame::Update( void )
{
	// Refresh the XAudio2 buffers
	m_pXA->Update();


	// Calculate the elapsed time between frames (in seconds)
	DWORD dwNow = GetTickCount();
	float fElapsedTime = (dwNow - m_dwCurrTime) / 1000.0f;
	m_dwCurrTime = dwNow;


	// Cap the elapsed time at 1/8th of a second
	if( fElapsedTime > 0.125f )
		fElapsedTime = 0.125f;


	// Increase the FPS timer
	m_fFPSTimer += fElapsedTime;
	if( m_fFPSTimer >= 1.0f )
	{
		m_unFPS		= m_unFrames;
		m_unFrames	= 0;
		m_fFPSTimer	= 0.0f;
	}


	// Update the fire timer
	m_fFireTimer += fElapsedTime;
	if( m_fFireTimer >= 3.0f )
	{
		m_fFireTimer = 0.0f;

	}




	// Update entities
	m_pEM->UpdateAll( fElapsedTime );
	m_pEM->CheckAllCollisions();

	// Process the messages & events
	m_pMS->ProcessMessages();
	m_pES->ProcessEvents();
}


//**************************************************
// Render
//	- render game entities
void CGame::Render( void )
{
	// Clear the background
	m_pD3D->Clear( D3DCOLOR_XRGB( 100, 100, 100 ) );	// Gray

	m_pD3D->DeviceBegin();	// Start the D3D Rendering
	m_pD3D->SpriteBegin();	// Start the sprite rendering (for images)



	// Render the FPS
	++m_unFrames;

	TOSTRINGSTREAM output;
	output << _T("FPS: ") << m_unFPS;

	m_pD3D->DrawText( output.str().c_str(), 0, 0 );



	// Render the entities
	m_pEM->RenderAll();





	m_pD3D->SpriteEnd();	// End the sprite rendering
	m_pD3D->DeviceEnd();	// End the D3D rendering
	m_pD3D->Present();		// Present the backbuffer
}


//**************************************************
// MessageProc
//	- callback function to process messages
//	- static method DOES NOT HAVE AN INVOKING OBJECT!!!
/*static*/ void CGame::MessageProc( IMessage* pMsg )
{
	// What type of message?
	switch( pMsg->GetMessageID() )
	{
	
		
		// ...
	default:
		{
			assert( false && "CGame::MessageProc - unknown message type" );
		}
		break;
	}
}
